import socket
import struct
import json
import http.client
import http.server
import socketserver
import os
import threading
import time
import tkinter as tk
from queue import Queue

# 多播相关设置
MULTICAST_GROUP_IP = '224.0.0.251'
MULTICAST_PORT = 5353

# HTTP 服务器相关设置
OTA_FILENAME = 'ota.bin'
OTA_FILEPATH = './uploads/ota.bin'
HTTP_SERVER_PORT = 3000

# HTTP 客户端相关设置
TARGET_DEVICE_IP = '192.168.43.1'  # 这里应替换为目标设备的 IP 地址
OTA_TRIGGER_PORT = 8080

# 版本信息
current_version = None
new_version = None

# 日志队列
log_queue = Queue()

def log_message(msg):
    log_queue.put(msg)

# 多播接收线程
def multicast_listener():
    global current_version, new_version
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(('', MULTICAST_PORT))
    group = socket.inet_aton(MULTICAST_GROUP_IP)
    mreq = struct.pack('4sL', group, socket.INADDR_ANY)
    sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
    log_message(f"Listening for multicast messages on {MULTICAST_GROUP_IP}:{MULTICAST_PORT}...")
    try:
        while True:
            data, addr = sock.recvfrom(1024)
            log_message(f"Received message from {addr}:")
            log_message(f"Raw data: {repr(data)}")
            try:
                json_data = json.loads(data.decode('utf-8'))
                ota_state = json_data.get('OTA_state')
                ota_version = json_data.get('OTA_version')
                if ota_state and ota_version:
                    log_message(f"OTA State: {ota_state}, Version: {ota_version}")
                    if current_version is None:
                        current_version = ota_version
                        log_message(f"Initial Version: {current_version}")
                    else:
                        new_version = ota_version
                        log_message(f"New Version: {new_version}")
                        if new_version > current_version:
                            log_message("Upgrade successful")
                        else:
                            log_message("Upgrade failed or version unchanged")
                else:
                    log_message("Received message is not in the expected format")
            except json.JSONDecodeError:
                log_message("Received message is not valid JSON")
            log_message("")
    except KeyboardInterrupt:
        log_message("Exiting...")
    finally:
        sock.setsockopt(socket.IPPROTO_IP, socket.IP_DROP_MEMBERSHIP, mreq)
        sock.close()

# HTTP服务器部分
class MyHttpRequestHandler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/api/uploadfile':
            if not os.path.exists(OTA_FILEPATH):
                self.send_error(404, 'File Not Found')
                return
            file_size = os.path.getsize(OTA_FILEPATH)
            self.send_response(200)
            self.send_header('Content-Disposition', f'attachment; filename="{OTA_FILENAME}"')
            self.send_header('Content-Type', 'application/octet-stream')
            self.send_header('Content-Length', file_size)
            self.end_headers()
            bytes_sent = 0
            with open(OTA_FILEPATH, 'rb') as f:
                while True:
                    chunk = f.read(4096)
                    if not chunk:
                        break
                    bytes_sent += len(chunk)
                    progress = (bytes_sent / file_size) * 100
                    log_message(f"Sending {len(chunk)} bytes... ({progress:.2f}%)")
                    self.wfile.write(chunk)
            log_message("File download complete")
        else:
            super().do_GET()

# HTTP客户端部分
def trigger_ota(target_ip):
    try:
        conn = http.client.HTTPConnection(f'{target_ip}:{OTA_TRIGGER_PORT}')
        conn.request('POST', '/ota-start', body='start_ota', headers={'Content-Type': 'text/plain'})
        response = conn.getresponse()
        log_message(f'Status: {response.status}, Response: {response.read().decode()}')
        conn.close()
    except Exception as e:
        log_message(f'Error occurred: {e}')
    log_message("")

def run_server():
    try:
        server = socketserver.TCPServer(('', HTTP_SERVER_PORT), MyHttpRequestHandler)
        log_message(f"Server running on port {HTTP_SERVER_PORT}")
        server.serve_forever()
    except Exception as e:
        log_message(f'Server error: {e}')

if __name__ == '__main__':
    if not os.path.exists('uploads'):
        os.makedirs('uploads')

    root = tk.Tk()
    root.title("科奈信8735b固件升级程序")
    root.geometry("600x400")

    log_frame = tk.Frame(root)
    log_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
    log_label = tk.Label(log_frame, text="运行日志:")
    log_label.pack(anchor=tk.W)
    scrollbar = tk.Scrollbar(log_frame)
    scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
    log_text = tk.Text(log_frame, wrap=tk.WORD, state=tk.DISABLED, yscrollcommand=scrollbar.set)
    log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
    scrollbar.config(command=log_text.yview)

    def update_log():
        while not log_queue.empty():
            message = log_queue.get()
            log_text.config(state=tk.NORMAL)
            log_text.insert(tk.END, message + '\n')
            log_text.config(state=tk.DISABLED)
            log_text.see(tk.END)
        root.after(100, update_log)

    update_log()

    multicast_thread = threading.Thread(target=multicast_listener)
    multicast_thread.daemon = True
    multicast_thread.start()
    log_message("Started multicast listener thread")

    server_thread = threading.Thread(target=run_server)
    server_thread.daemon = True
    server_thread.start()

    log_message("Waiting for initial version information...")
    root.after(2000, lambda: trigger_ota(TARGET_DEVICE_IP))
    log_message("Triggering OTA update...")

    def finish_upgrade():
        log_message("Waiting for device to complete upgrade...")
    root.after(12000, finish_upgrade)

    root.mainloop()
